﻿/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is tyzoh.jp code.
 *
 * The Initial Developer of the Original Code is Kazutaka Kurihara.
 * Portions created by the Initial Developer are Copyright (C) 2007
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * ***** END LICENSE BLOCK ***** */
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections;
using System.Windows.Forms;
using Qurihara.QBasic;
using Qurihara.Anm;
//using Microsoft.Ink;

namespace Qurihara
{
	/// <summary>
	/// FigureElement の概要の説明です。
	/// </summary>
	public abstract class FigureElement : IAnmElement
	{
		public FigureElement(Control parentCont,int prio)
		{
			// 
			// TODO: コンストラクタ ロジックをここに追加してください。
			//
			parentControl = parentCont;
			midPoint = new Point();
			boundingBox = new Rectangle();
			if (prio != -1) Priority = prio; // -1ならデフォルト（実装甘いですが．）
			else priority = GetNewMaxPriority();
		}
		protected Rectangle boundingBox;
		protected int priority=0;// = 3;
		protected Point midPoint;
		protected bool highlight = false;
		protected bool findable = true; // enables IsInside()
		protected bool drawEdge = true;
        //protected bool editable = false;
		protected bool visible = true;
        protected string tag = "";

		public abstract void draw(Graphics g);
        public abstract bool IsInside(int x, int y, bool coerceWheneverFindable);
		public abstract void moveRerative(int addx,int addy);
		public abstract void createCopy(out FigureElement fe);
		public abstract void createCreateEvent(FigureElementList fel,out CreateObjectEvent coe);
		//public abstract void createCreateEvent(FigureElementList fel,ref QArrayList eventListToAdd);
		protected static Pen pen_black = new Pen(Color.Black,3);
		protected static Pen pen_red = new Pen(Color.Red,3);
		protected static Pen pen_blue = new Pen(Color.Blue,3);
		protected static Font defaultFont = new Font("Times", 28, FontStyle.Regular,GraphicsUnit.Pixel);
		protected static Brush defaultBrush = new SolidBrush(Color.FromArgb(255,Color.Red));

		protected static int minPriority = -2;
		protected static int maxPriority = 0;
		public static int GetNewMinPriority()
		{
			int m = minPriority;
			if(minPriority > Int32.MinValue)
				minPriority--;
			return m;
		}
		public static int GetNewMaxPriority()
		{
			int m = maxPriority;
			if (maxPriority < Int32.MaxValue) 
				maxPriority++;
			return m;
		}
		public static void ResetPriorityCounter()
		{
			minPriority = -2;
			maxPriority = 0;
		}
		protected static Qurihara.InkView view;
		public static Qurihara.InkView View
		{
			get{return view;}
			set{view = value;}
		}

		protected static bool lockInvalidate = false;
		public static bool LockInvalidate
		{
			get{return lockInvalidate;}
			set{
				lockInvalidate = value;
				if (lockInvalidate == true)
				{
					//invalidateElementQueue.Clear();
				}
				else
				{
					FigureElement fe;
					while(invalidateElementQueue.Count > 0)
					{
						fe = (FigureElement)invalidateElementQueue.Dequeue();
						fe.Invalidate(); // 本当はrを保存しとかないと
					}
				}
			}
		}
		protected static Queue invalidateElementQueue = new Queue();
		// IAnm interface
		protected IAnm _anm;
		public IAnm Anm
		{
			get{return _anm;}
			set{_anm=value;}
		}

		protected float alpha = 1.0f;
		public virtual float Alpha
		{
			get{return alpha;}
			set{alpha = value;}
		}
		protected Control parentControl;
		public void Invalidate()
		{
			Invalidate(boundingBox);
		}
		public void Invalidate(Rectangle r)
		{
			if (parentControl != null) 
			{
				if (lockInvalidate == false)
				{
					Rectangle ri = new Rectangle(r.Location,r.Size);
					view.ViewToScreen(ref ri);
					ri.Inflate(30,30);
					parentControl.Invalidate(ri);
					/*
					parentControl.CreateGraphics().DrawRectangle(Pens.Red,ri);
					parentControl.Invalidate();
					*/
				}
				else
				{
					invalidateElementQueue.Enqueue(this);
				}
			}
		}

        public bool IsBoxedBy(Rectangle box, bool coerceWheneverFindable)
		{
			if (!findable && !coerceWheneverFindable) return false;
			// まったく同じ大きさのものは含めない。 →含める
			//if (box.Equals(boundingBox)) return false;
			return box.Contains(boundingBox);
		}
		public bool Highlight
		{
			get {return highlight;}
			set 
			{
				if (highlight != value)
				{
					highlight = value;
					HighlightAnimationState=0;
					Invalidate();
				}
			}
		}
		public bool Findable
		{
			get {return findable;}
			set {findable = value;}
		}
		public bool DrawEdge
		{
			get {return drawEdge;}
			set {drawEdge = value;}
		}
        //public bool Editable
        //{
        //    get {return editable;}
        //    set {editable = value;}
        //}
		public bool Visible
		{
			get{return visible;}
			set{visible = value;}
		}
		static int MAX_ANM  = 2;
		protected int HighlightAnimationState;
		public void HighlightAnimationStep()
		{
			HighlightAnimationState++;
			if (HighlightAnimationState == FigureElement.MAX_ANM)
				HighlightAnimationState =0;
		}
		public Point MidPoint
		{
			get
			{
				midPoint.X = (2*boundingBox.X+boundingBox.Width)/2;
				midPoint.Y = (2*boundingBox.Y+boundingBox.Height)/2;
				return midPoint;
			}
		}
		public Rectangle BoundingBox
		{ //set するならResize()を呼べ！
			get
			{
				return boundingBox;
			}
			set
			{// 040714
				Resize(value.X-boundingBox.X,
					value.Y-boundingBox.Y,
					value.Width-boundingBox.Width,
					value.Height-boundingBox.Height);
			}
		}
		public int Priority
		{
			get{ return priority;}
			set{
				priority = value;
				if (priority >= maxPriority) maxPriority = priority+1;
				if (priority <= minPriority) minPriority = priority-1;
			}
		}
        public string Tag
        {
            get { return tag; }
            set{ tag = value;}
        }
		protected virtual void highlightDraw(Graphics g)
		{
			if (this.highlight && this.HighlightAnimationState == 0)
			{
				pen_red.Width = (int)(3 * view.ScreenToViewRatio.X);
				g.DrawRectangle(pen_red,boundingBox);
				highlightEdgeDraw(g,pen_red);
			} 
			if (this.highlight && this.HighlightAnimationState == 1)
			{
				pen_black.Width = (int)(3 * view.ScreenToViewRatio.X);
				g.DrawRectangle(pen_black,boundingBox);
				highlightEdgeDraw(g,pen_black);
			} 
		}
		protected static int edgeRectWidth = 10;
		protected virtual void highlightEdgeDraw(Graphics g,Pen pen)
		{
			if (drawEdge)
			{
//				g.DrawEllipse(pen,boundingBox.X-5,boundingBox.Y-5,10,10);
//				g.DrawEllipse(pen,boundingBox.X+boundingBox.Width-5,boundingBox.Y-5,10,10);
//				g.DrawEllipse(pen,boundingBox.X-5,boundingBox.Y+boundingBox.Height-5,10,10);
//				g.DrawEllipse(pen,boundingBox.X+boundingBox.Width-5,boundingBox.Y+boundingBox.Height-5,10,10);
				int wid = (int)(edgeRectWidth * view.ScreenToViewRatio.X);
				g.DrawRectangle(pen,boundingBox.X,boundingBox.Y,wid,wid);
				g.DrawRectangle(pen,boundingBox.X+boundingBox.Width-wid,boundingBox.Y,wid,wid);
				g.DrawRectangle(pen,boundingBox.X,boundingBox.Y+boundingBox.Height-wid,wid,wid);
				g.DrawRectangle(pen,boundingBox.X+boundingBox.Width-wid,boundingBox.Y+boundingBox.Height-wid,wid,wid);
			}
		}
		public virtual void Resize(int x,int y,int w,int h)
		{
			if(x !=0 || y!=0 || w!= 0 || h!= 0)
			{
				Invalidate();
				boundingBox.X += x;
				boundingBox.Y += y;
				boundingBox.Width += w;
				boundingBox.Height += h;
				Invalidate();
			}
		}
		/*
		public virtual void MoveTo(int x,int y)
		{
			boundingBox.X = x;
			boundingBox.Y = y;
		}
		*/

		public enum EDGE
		{
			NO_EDGE,
			LEFTUP,
			RIGHTUP,
			LEFTDOWN,
			RIGHTDOWN,
		}
//		public static int NEAR = 8;
        public virtual EDGE PointEdge(int x, int y, bool coerceWheneverFindable)
		{
			if (!findable && !coerceWheneverFindable) return EDGE.NO_EDGE;

			int wid = (int)(edgeRectWidth * view.ScreenToViewRatio.X);
			Rectangle r = new Rectangle(boundingBox.X,boundingBox.Y,wid,wid);
			if (r.Contains(x,y)) return EDGE.LEFTUP;
			r.X =boundingBox.X+boundingBox.Width-wid;
			if (r.Contains(x,y)) return EDGE.RIGHTUP;
			r.Y = boundingBox.Y+boundingBox.Height-wid;
			if (r.Contains(x,y)) return EDGE.RIGHTDOWN;
			r.X = boundingBox.X;
			if (r.Contains(x,y)) return EDGE.LEFTDOWN;

//			Qurihara.QBasic.Vector2 v = new Vector2();
//			// check leftup
//			v.setVec((double)(x-boundingBox.X),(double)(y-boundingBox.Y));
//			v.calcNorm();
//			if ( v.Norm < NEAR)
//			{
//				return EDGE.LEFTUP;
//			}
//			v.setVec((double)(x-boundingBox.X-boundingBox.Width),(double)(y-boundingBox.Y));
//			v.calcNorm();
//			if ( v.Norm < NEAR)
//			{
//				return EDGE.RIGHTUP;
//			}
//			v.setVec((double)(x-boundingBox.X),(double)(y-boundingBox.Y-boundingBox.Height));
//			v.calcNorm();
//			if ( v.Norm < NEAR)
//			{
//				return EDGE.LEFTDOWN;
//			}
//			v.setVec((double)(x-boundingBox.X-boundingBox.Width),(double)(y-boundingBox.Y-boundingBox.Height));
//			v.calcNorm();
//			if ( v.Norm < NEAR)
//			{
//				return EDGE.RIGHTDOWN;
//			}
			return EDGE.NO_EDGE;
		} 
		public virtual bool IsPointingEditButton(int x,int y, bool coerceWheneverFindable)
		{
            if ((!findable && !coerceWheneverFindable) || !highlight) return false;
            //if (!findable || !editable || !highlight) return false;
			//if (this.highlight)
			//{
				Qurihara.QBasic.Vector2 v = new Vector2();
				v.setVec((double)(x-(boundingBox.X+5+20)),(double)(y-(boundingBox.Y+5+20)));
				v.calcNorm();
				if ( v.Norm < 20)
				{
					return true;
				}
			//}
			return false;
		}
		protected virtual void EditButtonDraw(Graphics g)
		{
			/*
			if (this.highlight && this.editable)
			{
				g.DrawEllipse(pen_red,boundingBox.X+5,boundingBox.Y+5,40,40);
				g.DrawString("言",defaultFont,defaultBrush,boundingBox.X+5,boundingBox.Y+5+5);
			}
			*/		
		}
		public virtual Point GetNearestPointOnBoundsFrom(Point p)
		{
			Point O = new Point(MidPoint.X,MidPoint.Y);
			p.X -= O.X;
			p.Y -= O.Y;
			p.Y *= -1;
			Point l = new Point(BoundingBox.Right - O.X,BoundingBox.Top - O.Y);
			l.Y *= -1;
			float f1 = (float)l.Y / l.X * p.X - p.Y;
			float f2 = -(float)l.Y / l.X * p.X - p.Y;
			Point pOut = new Point();
			if (f1 >= 0)
			{
				if (f2 >= 0)
				{ // on bottom edge
					pOut.Y = BoundingBox.Bottom;
					pOut.X = (int)((float)p.X / p.Y * (-l.Y) + O.X); 
				}
				else
				{ // on right edge
					pOut.X = BoundingBox.Right;
					pOut.Y = (int)((float)p.Y / p.X * l.X);
					pOut.Y = -pOut.Y + O.Y;
				}
			}
			else
			{
				if (f2 >= 0)
				{ // on left edge
					pOut.X = BoundingBox.Left;
					pOut.Y = (int)((float)p.Y / p.X * (-l.X));
					pOut.Y = -pOut.Y + O.Y;
				}
				else
				{ // on top edge
					pOut.Y = BoundingBox.Top;
					pOut.X = (int)((float)p.X / p.Y * l.Y + O.X);
				}
			}
			return pOut;
		}
	}
// ***********************
	public interface IHasFillColor // ぬり色情報を持つエレメントが実装する。
	{
		Color FillColor
		{
			get;
			set;
		}
	}
	public interface IHasOutLineColor // ふちどり色情報を持つエレメントが実装する。
	{
		Color OutLineColor
		{
			get;
			set;
		}
		int OutLineWidth
		{
			get;
			set;
		}
	}
	public interface IHasText : IHasFillColor// テキスト情報を持つエレメントが実装する。
	{
		Font Font
		{
			get;
			set;
		}

		string Text
		{
			get;
			set;
		}
	}

	// ******************************
	public abstract class WakuElement : FigureElement
	{
		protected int x1,x2,y1,y2;
		/*		public WakuElement(int _x1,int _y1,int _x2,int _y2) :base()
				{
					x1=_x1;
					y1=_y1;
					x2=_x2;
					y2=_y2;
					setBoundingBox();
				}*/
		public WakuElement(Control parentCont,int prio,Rectangle r) :base(parentCont,prio)
		{
			x1=r.X;
			y1=r.Y;
			x2=r.X+r.Width;
			y2=r.Y+r.Height;
			setBoundingBox();
		}
		protected void setBoundingBox()
		{
			boundingBox.X = x1;
			boundingBox.Y = y1;
			boundingBox.Width = x2-x1;
			boundingBox.Height = y2-y1;
		}
		public override void moveRerative(int addx,int addy)
		{
			if(addx !=0 || addy !=0)
			{
				Invalidate();
				x1 += addx;
				x2 += addx;
				y1 += addy;
				y2 += addy;
				setBoundingBox();
				Invalidate();
			}
		}
		public override void Resize(int x,int y,int w,int h)
		{
			if(x !=0 || y!=0 || w!= 0 || h!= 0)
			{
				Invalidate();
				x1 += x;
				y1 += y;
				x2 += (x+w);
				y2 += (y+h);
				setBoundingBox();
				Invalidate();
			}
		}
        public override bool IsInside(int x, int y, bool coerceWheneverFindable)
		{
			if (!findable && !coerceWheneverFindable) return false;
			if (x1 <= x && x <= x2 && y1 <= y && y <= y2) return true;
			else return false;
		}
	}

	// ******************************
	public abstract class ColorWakuElement : WakuElement, IHasFillColor, IHasOutLineColor, IPlayBackAnmElement
	{
		protected Color fillColor;
		protected Color outLineColor;
		protected int outLineWidth = 1;
		protected Pen pen;
		protected SolidBrush brush;
		public Color FillColor
		{
			get{return fillColor;}
			set{
				fillColor = value;
				ResetBrush();
				//if (brush != null) brush.Dispose();
				//brush = new SolidBrush(fillColor);
			}
		}
		protected void ResetBrush()
		{
			//if (brush != null) brush.Dispose();
			brush = new SolidBrush(Color.FromArgb((int)(255*playPosition*alpha),fillColor));

		}
		public Color OutLineColor
		{
			get{return outLineColor;}
			set
			{
				outLineColor = value;
				ResetPen();
			}
		}
		public int OutLineWidth
		{
			get{return outLineWidth;}
			set
			{
				outLineWidth = value;
				ResetPen();
			}
		}
		protected void ResetPen()
		{
			//if (pen != null) pen.Dispose();
			if (outLineWidth == 0) pen = Pens.Black; // dummy
			else pen = new Pen(Color.FromArgb((int)(255*playPosition*alpha),outLineColor),outLineWidth);
		}
				
		public ColorWakuElement(Control parentCont,int prio,Rectangle r,Color fc,Color oc,int ow) :base(parentCont,prio,r)
		{
			fillColor = fc;
			brush = new SolidBrush(fillColor);
			outLineColor = oc;
			outLineWidth = ow;
			pen = new Pen(outLineColor,outLineWidth);
            //editable = true;
		}

		protected float playPosition = 1.0f;
		public float PlayPosition
		{
			get{return playPosition;}
			set{
				playPosition = ZeroToOne(value);
				ResetBrush();
				ResetPen();
				}
		}

		public override float Alpha
		{
			get{return alpha;}
			set{alpha = ZeroToOne(value);
				ResetBrush();
				ResetPen();
			}
		}
		protected float ZeroToOne(float f)
		{
			if (f<0f) return 0f;
			if (f>1f) return 1f;
			return f;
		}

	}

	// ******************************
	public class RectangleElement : ColorWakuElement
	{
		public RectangleElement(Control parentCont,int prio,Rectangle r,Color fc,Color oc,int ow) : base(parentCont,prio,r,fc,oc,ow)
		{
		}
		public override void draw(Graphics g)
		{
			if (visible)
			{
				g.FillRectangle(brush,boundingBox);
				if (outLineWidth > 0) g.DrawRectangle(pen,boundingBox);
				highlightDraw(g);
				EditButtonDraw(g);
			}
		}
		public override void createCopy(out FigureElement fe)
		{
			fe = (FigureElement)(new RectangleElement(this.parentControl,this.priority,this.boundingBox,this.fillColor,this.outLineColor,this.outLineWidth));
		}
		public override void createCreateEvent(FigureElementList elementList, out CreateObjectEvent coe)
		{
			coe = (CreateObjectEvent)(new CreateRectangleEvent(elementList,this));
		}
	}
	// ******************************
	public class EllipseElement : ColorWakuElement
	{
		public EllipseElement(Control parentCont,int prio,Rectangle r,Color fc,Color oc,int ow) : base(parentCont,prio,r,fc,oc,ow)
		{
		}
		public override void draw(Graphics g)
		{
			if(visible)
			{
				g.FillEllipse(brush,boundingBox);
				if (outLineWidth > 0) g.DrawEllipse(pen,boundingBox);
				this.highlightDraw(g);
				EditButtonDraw(g);
			}
		}
		protected override void highlightDraw(Graphics g)
		{
			if (this.highlight && this.HighlightAnimationState == 0)
			{
				pen_red.Width = (int)(3 * view.ScreenToViewRatio.X);
				g.DrawEllipse(pen_red,boundingBox);
				highlightEdgeDraw(g,pen_red);
			}
			if (this.highlight && this.HighlightAnimationState == 1)
			{
				pen_black.Width = (int)(3 * view.ScreenToViewRatio.X);
				g.DrawEllipse(pen_black,boundingBox);
				highlightEdgeDraw(g,pen_black);
			}
		}
		public override bool IsInside(int x,int y,bool coerceWheneverFindable)
		{
			if (!findable && !coerceWheneverFindable) return false;

			double a,b,f,xx,yy;
			xx = x - (x1+x2)/2.0;
			yy = y - (y1+y2)/2.0;
			a=boundingBox.Width/2.0;
			b=boundingBox.Height/2.0;
			f=b*b*xx*xx+a*a*yy*yy - a*a*b*b;

			// ispointingEditButtonを呼ぶのはとてもむだであろう。
			if (f <= 0 || this.IsPointingEditButton(x,y,coerceWheneverFindable)) return true;
			else return false;
		}
		public override void createCopy(out FigureElement fe)
		{
			fe = (FigureElement)(new EllipseElement(this.parentControl,this.priority,this.boundingBox,this.fillColor,this.outLineColor,this.outLineWidth));
		}
		public override void createCreateEvent(FigureElementList elementList,out CreateObjectEvent coe)
		{
			coe = (CreateObjectEvent)(new CreateEllipseEvent(elementList,this));
		}
/*
		public override void createCreateEvent(FigureElementList elementList,ref QArrayList eventListToAdd )
		{
			eventListToAdd.Add(new CreateEllipseEvent(elementList,this));
		}
*/
		/* 作ったが、無限大点があるのでだめ。矢印が上下方向になるとき。
		public override Point GetNearestPointOnBoundsFrom(Point po)
		{
			Point p = new Point(po.X,po.Y);
			Point O = new Point(MidPoint.X,MidPoint.Y);
			Point AB = new Point(BoundingBox.Right - O.X
				,BoundingBox.Bottom - O.Y);
			p.X -= O.X;
			p.Y -= O.Y;
			Point pOut= new Point();
			double bab2 = (double)p.Y / p.X / AB.Y;
			bab2 *= bab2;
			pOut.X = Math.Abs((int)(AB.X * Math.Sqrt(1 - bab2)));
			pOut.Y = Math.Abs((int)((float)p.Y / p.X * pOut.X));

			if (p.X > 0)
			{
				if (p.Y > 0)
				{
				}
				else
				{
					pOut.Y *= -1;
				}
			}
			else
			{
				if (p.Y > 0)
				{
					pOut.X *= -1;
				}
				else
				{
					pOut.X *= -1;
					pOut.Y *= -1;
				}
			}
			pOut.X += O.X;
			pOut.Y += O.Y;
			return pOut;
		}
		*/
	}
	// ******************************
	public class BitmapElement : WakuElement// , IPlayBackAnmElement
	{
		protected Bitmap image;
        protected string imageString = "";
		public BitmapElement(Control parentCont,int prio,Rectangle r) : base(parentCont,prio,r)
		{
			image = null;
		}
		public BitmapElement(Control parentCont,int prio,Rectangle r,Bitmap bm) : base(parentCont,prio,r)
		{
            BitMap = bm;
			//image = bm;
			//Bitmap image = new Bitmap(@"c:\tmp\サンプル.jpg");
		}
		/*
		protected float playPosition = 1.0f;
		public float PlayPosition
		{
			get{return playPosition;}
			set
			{
				float fl = (float)value;
				if (fl<0f) fl = 0f;
				if (fl>1f) fl = 1.0f;
				playPosition = fl;
				ResetBrush();
			}
		}
		public override float Alpha
		{
			get{return alpha;}
			set
			{
				float fl = (float)value;
				if (fl<0f) fl = 0f;
				if (fl>1f) fl = 1.0f;
				alpha = fl;
				ResetBrush();
			}
		}
		protected SolidBrush brush;
		protected void ResetBrush()
		{
			brush = new SolidBrush(Color.FromArgb((int)(255*playPosition*alpha),Color.White));
		}
*/
		public override void draw(Graphics g)
		{
			if (visible)
			{
                if (ImageAnimator.CanAnimate(image))
                {
                    ImageAnimator.UpdateFrames(image);
                }

				g.DrawImage(image,BoundingBox);
/*
				if (alpha < 1.0f || playPosition < 1.0f)
				{
					g.FillRectangle(brush,BoundingBox);
				}
*/
				highlightDraw(g);
			}
		}
		/*
		public void setBitMap(Bitmap bm)
		{
			image = bm;
		}
		*/
		public Bitmap BitMap
		{
			get
			{
				return image;
			}
			set
			{
				image = value;
                if (QBasic.Util.IsPropertyTrue("Base.HasSerializedBitmap"))
                {
                    //imageString = QBasic.DrawingUtil.SOAPSerialize(image);
                    imageString = QBasic.DrawingUtil.BitmapBase64Serialize(image);
                }

                if (ImageAnimator.CanAnimate(image))
                {
                    ImageAnimator.Animate(image,
                        new EventHandler(this.Image_FrameChanged));
                }
			}
		}
        private void Image_FrameChanged(object o, EventArgs e)
        {
            parentControl.Invalidate();
            //this.Invalidate();
        }

        public string ImageString
        {
            get { return imageString; }
        }
        public override bool IsPointingEditButton(int x, int y, bool coerceWheneverFindable)
		{
			return false;
		}

		public override void createCopy(out FigureElement fe)
		{
			fe = (FigureElement)(new BitmapElement(this.parentControl,this.priority,this.boundingBox,this.BitMap));
		}
		public override void createCreateEvent(FigureElementList elementList,out CreateObjectEvent coe)
		{
			coe = (CreateObjectEvent)(new CreateBitmapEvent(elementList,this));
			//coe = (CreateObjectEvent)(new CreateInkEvent(elementList,this));
		}
/*
		public override void createCreateEvent(FigureElementList elementList,ref QArrayList eventListToAdd )
		{
			eventListToAdd.Add(new CreateBitmapEvent(elementList,this));
		}
*/
	}

	// ****
	public class ExternalElement : WakuElement
	{
		public string filename;
		protected SolidBrush brush;
		protected SolidBrush tbrush;
		protected Font tFont;
		protected int fontsize;
		protected string tFile;
		public ExternalElement(Control parentCont,int prio,Rectangle r) : base(parentCont,prio,r)
		{
			filename = null;
			setForDraw();
			//if (prio < 0) priority = 1;
		}
		public ExternalElement(Control parentCont,int prio,Rectangle r,string name) : base(parentCont,prio,r)
		{
			filename = name;
			setForDraw();
		}
		protected void setForDraw()
		{
			//brush = new SolidBrush(Color.FromArgb(200,Color.LightGreen));
			brush = new SolidBrush(Color.LightGreen);
			tbrush = new SolidBrush(Color.Black);
			fontsize = 16;
			tFont = new Font("MS UI Gothic", fontsize, FontStyle.Bold,GraphicsUnit.Pixel);
			if (Qurihara.QBasic.Util.IsPropertyTrue("Base.LocalApp"))
			{
				//下の関数に退避させた。
				tFile = _tFile(tFile);
			}
			else
			{
				tFile = "";//System.IO.Path.GetFileName(filename);
			}
			//string [] s = filename.Split('\\');
			//tFile = s[s.Length-1];
		}
		protected virtual string _tFile(string tf)
		{
			return QBasic.Util.GetShortDisplayName(
				QBasic.Util.getFileNameWithoutPath(
				System.Web.HttpUtility.UrlDecode(
				filename
				)
				)
				,18);
		}
		public override void draw(Graphics g)
		{
			if (visible)
			{
				Point [] p = {new Point(boundingBox.X,boundingBox.Y),
								 new Point(boundingBox.X+boundingBox.Width,boundingBox.Y+boundingBox.Height/2),
								 new Point(boundingBox.X,boundingBox.Y+boundingBox.Height),
				};
				g.FillPolygon(brush,p);
				g.DrawPolygon(Pens.Black,p);

				g.DrawString(tFile,tFont
					,tbrush,boundingBox.X,boundingBox.Y+boundingBox.Height/2-fontsize/2);

				highlightDraw(g);
			}
		}
		public override bool IsPointingEditButton(int x,int y,bool coerceWheneverFindable)
		{
			return false;
		}
		public override void createCopy(out FigureElement fe)
		{
			fe = (FigureElement)(new ExternalElement(this.parentControl,this.priority,this.boundingBox,this.filename));
		}
		public override void createCreateEvent(FigureElementList elementList,out CreateObjectEvent coe)
		{
			coe = (CreateObjectEvent)(new CreateExternalEvent(elementList,this));
		}
/*
		public override void createCreateEvent(FigureElementList elementList,ref QArrayList eventListToAdd )
		{
			eventListToAdd.Add(new CreateExternalEvent(elementList,this));
		}
*/
//		public static bool IsMacroExternalElement(string filename, out string [] argV)
//		{
//			string s = QBasic.Util.getFileNameWithoutPath(filename);
//			if (s.StartsWith("[") && s.EndsWith("]")) 
//			{
//				s = s.Substring(1);
//				if (s.Length > 1) s = s.Substring(0,s.Length-1);
//				argV = s.Split(':');
//				return true;
//			}
//			else
//			{
//				argV = null;
//				return false;
//			}
//		}

		public event System.EventHandler ClickedHandler;
		private EventHandler pch;
		private IAnm anm;
        private bool prevFindable;
		public void Clicked()
		{
			anm = new Qurihara.Anm.JumpAnm(this,BoundingBox.Height,500);
			pch = new EventHandler(anm_AnmFinishedHandler);
			anm.AnmFinishedHandler += pch;
            prevFindable = findable;
            findable = false;// !findable;
			anm.Start();
		}
		private void anm_AnmFinishedHandler(object sender, EventArgs e)
		{
			anm.AnmFinishedHandler -= pch;
            findable = prevFindable;// !findable;
			if ( ClickedHandler != null) ClickedHandler(this,new EventArgs());
		}
/*
		// クリックされたときの動作．ここに記述すべきでないような気もする．
		private EventHandler eh;
		private IAnm ianm;
		public void Clicked(IAnm anm)
		{
			ianm = anm;
			eh = new EventHandler(anm_AnmFinishedHandler);
			anm.AnmFinishedHandler += eh;
			findable = false;
			anm.Start();

		}

		private void anm_AnmFinishedHandler(object sender, EventArgs e)
		{
			ianm.AnmFinishedHandler -= eh;
			this.findable = true;
		}
*/
	}
	// ******************************
	/*
		public class TextElement : RectangleElement
		{
			protected string text;
			protected int fontsize;

			public bool multiline;

			public TextElement(Rectangle r,string t) : base(r)
			{
				text = t;
				int i;
				string  [] s = t.Split('\n');
				if (s.Length > 1)
				{
					multiline = true;

					int longest = 0;
					for (i=0; i< s.Length;i++)
					{
						if (s[longest].Length < s[i].Length) longest = i;
					}
					int widthsize,heightsize;
					widthsize = BoundingBox.Width/(s[longest].Length+1);
					heightsize = BoundingBox.Height/s.Length;
					fontsize = (widthsize > heightsize)? heightsize : widthsize;
					y1 = y1 + BoundingBox.Height /2 - fontsize*s.Length/2 -1;
					y2 = y2 - BoundingBox.Height /2 + fontsize*s.Length/2 +1;
					setBoundingBox();
				}
				else
				{
					multiline = false;
					fontsize = BoundingBox.Width/(text.Length+1);
					y1 = y1 + BoundingBox.Height / 2 - fontsize / 2 -1;
					y2 = y2 - BoundingBox.Height / 2 + fontsize / 2 +1;
					setBoundingBox();
				}

			}
			public override void draw(Graphics g)
			{
				StringFormat stringFormat = new StringFormat();
				//sf.Alignment = System.Drawing.StringAlignment.Center;
				if (multiline)
				{
					stringFormat.Alignment = System.Drawing.StringAlignment.Near;
					g.DrawString("・" + text.Replace("\n","\n・"),new Font("Times", fontsize, FontStyle.Regular,GraphicsUnit.Pixel),
						new SolidBrush(Color.Black),BoundingBox,stringFormat);
				}
				else
				{
					stringFormat.Alignment = System.Drawing.StringAlignment.Center;
					g.DrawString(text,new Font("Times", fontsize, FontStyle.Regular,GraphicsUnit.Pixel),
						new SolidBrush(Color.Black),BoundingBox,stringFormat);
				}
				highlightDraw(g);
			}
			public string Text
			{
				get
				{
					return text;
				}
				set
				{
					text =value;
				}
			}
			public override void createCopy(out FigureElement fe)
			{
				fe = (FigureElement)(new TextElement(this.boundingBox,this.text));
			}
		}
	*/
	// ******************************
	public class TextElement : WakuElement ,QBasic.BisectionFunction , IHasText ,IPlayBackAnmElement// , IHasColor
	{
		protected string text;
		protected int fontsize;
		protected Color fillColor;

		public bool multiline;
		//public Color color;
		protected Font font; // スタイルなどを保存する
		protected Font theFont; // 毎回のdrawで変化する。
		protected SizeF layoutSize;
		protected SizeF stringSize;
		protected Graphics graphics;
		protected StringFormat newStringFormat;

		protected static int A_LITTLE = 5;
		public TextElement(Control parentCont,int prio,Rectangle r,string t,Color c,Font f) : base(parentCont,prio,r)
		{
            //editable = true;
			fillColor = c;
			/*
						x1 += A_LITTLE;
						y1 += A_LITTLE;
						x2 -= A_LITTLE;
						y2 -= A_LITTLE;
						setBoundingBox();
			*/
			text = t;
			SetMultiLine();

			//color = c;
			font = f;

			//if (prio < 0) priority = 1;

		}
		protected void SetMultiLine()
		{
			string  [] s = text.Split('\n');
			if (s.Length > 1)
			{
				multiline = true;
			}
			else
			{
				multiline = false;
			}
		}
		public override void draw(Graphics g)
		{
			if (visible)
			{
				newStringFormat = new StringFormat();
				if (multiline)
				{
					newStringFormat.Alignment = System.Drawing.StringAlignment.Near;
				}
				else
				{
					newStringFormat.Alignment = System.Drawing.StringAlignment.Center;
				}

				// calc fontsize
				layoutSize = new SizeF((float)BoundingBox.Width, 1000.0F);
				//newStringFormat.FormatFlags = StringFormatFlags.NoClip;
				theFont = null;
				graphics = g;
				int minFontSize = 4;
				int maxFontSize = 640;
				if(QBasic.Bisection.Solve((BisectionFunction)this,ref minFontSize,ref maxFontSize)
					== Bisection.State.solved)
				{
					fontsize = minFontSize;
				}
				else
				{
					fontsize = 4;
				}
				func(minFontSize); // set format to the solution
				// for debug
				//g.DrawRectangle(Pens.Black,BoundingBox.X,BoundingBox.Y,layoutSize.Width,layoutSize.Height);
				//g.DrawRectangle(Pens.Blue,BoundingBox.X,BoundingBox.Y,stringSize.Width,stringSize.Height);
				// for debug fin

				// draw
				RectangleF rf = new RectangleF();
				rf.Width = stringSize.Width;
				rf.Height = stringSize.Height;
				rf.X = BoundingBox.X + (BoundingBox.Width - stringSize.Width)/2;
				rf.Y = BoundingBox.Y + (BoundingBox.Height - stringSize.Height)/2;

				Color c;
				if (this.highlight) 
				{
					c = Color.Red;
				}
				else
				{
					//c = this.fillColor;
					//c = Color.FromArgb((int)(255*playPosition),this.fillColor);
					c = Color.FromArgb((int)(255*playPosition*alpha),this.fillColor);
				}
				Brush b = new SolidBrush(c);
				g.DrawString(
					text,
					theFont,
					//				Brushes.Black,
					b,
					/*
									new RectangleF(
										BoundingBox.X,//+fontsize/3,
										BoundingBox.Y,
										stringSize.Width,
										stringSize.Height),
					*/
					rf,
					newStringFormat);
				highlightDraw(g);
				this.EditButtonDraw(g);

				theFont.Dispose();			
				newStringFormat.Dispose();
				b.Dispose();
			}
		}

		public int func(int x) // interface BisectionFunction
		{
			int charactersFitted;
			int linesFilled;

			if (theFont != null) theFont.Dispose();
			theFont = new Font(font.Name, x,font.Style);

			stringSize = graphics.MeasureString(
				text,
				theFont,
				layoutSize,
				newStringFormat,
				out charactersFitted,
				out linesFilled);

			if (stringSize.Height< BoundingBox.Height &&
				stringSize.Width < BoundingBox.Width
				) return -1;
			return 1;
		}

		public string Text
		{
			get
			{
				return text;
			}
			set
			{
				text =value;
				SetMultiLine();
			}
		}
		public override void createCopy(out FigureElement fe)
		{
			fe = (FigureElement)(new TextElement(this.parentControl,this.priority,this.boundingBox,this.text,this.fillColor,this.font));
		}
		public override void createCreateEvent(FigureElementList elementList,out CreateObjectEvent coe)
		{
			coe = (CreateObjectEvent)(new CreateTextEvent(elementList,this));
		}
/*
		public override void createCreateEvent(FigureElementList elementList,ref QArrayList eventListToAdd )
		{
			eventListToAdd.Add(new CreateTextEvent(elementList,this));
		}
*/
        public override bool IsPointingEditButton(int x, int y, bool coerceWheneverFindable)
		{
			if ((!findable && !coerceWheneverFindable) || !highlight) return false;
            //if (!findable || !editable || !highlight) return false;
            //if (this.highlight)
			//{
				Qurihara.QBasic.Vector2 v = new Vector2();
				v.setVec((double)(x-(boundingBox.X+boundingBox.Width-40-5+20)),(double)(y-(boundingBox.Y+5+20)));
				v.calcNorm();
				if ( v.Norm < 20)
				{
					return true;
				}
			//}
			return false;
		}

		protected override void EditButtonDraw(Graphics g)
		{
			/*
			if (this.highlight && this.findable)
			{
				g.DrawEllipse(pen_red,boundingBox.X+boundingBox.Width-40-5,boundingBox.Y+5,40,40);
				g.DrawString("編",defaultFont,defaultBrush,boundingBox.X+boundingBox.Width-40-5,boundingBox.Y+5+5);
			}
			*/		
		}

		public Font Font
		{
			get{return font;}
			set{font = value;}
		}
		public Color FillColor
		{
			get{return fillColor;}
			set{fillColor = value;}
		}

		protected float playPosition = 1.0f;
		public float PlayPosition
		{
			get{return playPosition;}
			set{playPosition = value;}
		}

	}

	// ******************************
	public class ArrowElement : FigureElement , IHasFillColor
	{
		public FigureElement fromElement,towardElement;
		private Point from_,toward_;
		protected Color fillColor;
		public System.Drawing.Drawing2D.DashStyle dashStyle;
		float sizeRatio = 0.85f;

		protected static Matrix22 arrowMat = new Matrix22(-1.0/2.0, -Math.Sqrt(3.0)/2.0,
			Math.Sqrt(3.0)/2.0, -1.0/2.0);
		protected Vector2 arrowVec;
        protected static int maxArrowWidth = 64;

		public ArrowElement(Control parentCont,int prio,FigureElement from,FigureElement toward,Color c,System.Drawing.Drawing2D.DashStyle ds) : base(parentCont,prio)
		{
			fromElement = from;
			towardElement = toward;
			from_ = new Point();
			toward_ = new Point();
			fillColor = c;
			dashStyle = ds;
			setBoundingBox();
			//if (prio < 0) priority = 2;
		}
		/*
				public ArrowElement(FigureElement from, int towardx, int towardy) : base()
				{
					fromElement = from;
					towardElement = new VirtualPointElement(towardx,towardy);
					setBoundingBox();
				}
				public ArrowElement(int fromx, int fromy, FigureElement toward) : base()
				{
					fromElement = new VirtualPointElement(fromx,fromy);
					towardElement = toward;
					setBoundingBox();
				}
		*/
		protected void setBoundingBox()
		{
			Point fromMid,towardMid;

			fromMid = fromElement.GetNearestPointOnBoundsFrom(towardElement.MidPoint);
			towardMid = towardElement.GetNearestPointOnBoundsFrom(fromElement.MidPoint);

			// 矢印の端点を計算する。
			// 縮小処理。適当。本来ならばオブジェクトの外郭を抽出すべき
			from_.X = (int)(sizeRatio * fromMid.X + (1-sizeRatio) * towardMid.X);
			from_.Y = (int)(sizeRatio * fromMid.Y + (1-sizeRatio) * towardMid.Y);
			toward_.X = (int)((1-sizeRatio) * fromMid.X + sizeRatio * towardMid.X);
			toward_.Y = (int)((1-sizeRatio) * fromMid.Y + sizeRatio * towardMid.Y);
			// boundingboxを計算。
			if (from_.X < toward_.X) 
			{
				boundingBox.X = from_.X;
				boundingBox.Width = toward_.X - from_.X;
			}
			else
			{
				boundingBox.X = toward_.X;
				boundingBox.Width = from_.X - toward_.X;
			}
			if (from_.Y < toward_.Y) 
			{
				boundingBox.Y = from_.Y;
				boundingBox.Height = toward_.Y - from_.Y;
			}
			else
			{
				boundingBox.Y = toward_.Y;
				boundingBox.Height = from_.Y - toward_.Y;
			}
		}
        protected Queue delayInvalidateQueue = new Queue();
		public override void draw(Graphics g)
		{
			if(visible)
			{
                Rectangle recInv = new Rectangle(boundingBox.Location, boundingBox.Size);
                setBoundingBox();
                if (recInv.Equals(boundingBox) == false)
                {
                    delayInvalidateQueue.Enqueue(recInv);
                    delayInvalidateQueue.Enqueue(boundingBox);
                    Qurihara.Anm.WaitAnm anm = new WaitAnm(50);
                    anm.AnmFinishedHandler += new EventHandler(anm_AnmFinishedHandler);
                    anm.Start();
                }
                //			check if fromElement and towardElement exist.

				// やじりの描画
				Vector2 vec = new Vector2(toward_.X-from_.X,toward_.Y-from_.Y);
				vec.calcNorm();
				double norm = vec.Norm;
                if (norm * 0.10 > maxArrowWidth) norm = maxArrowWidth * 10;
				vec.unitize();
				vec.mul(0.15*norm);			
				Point [] edge = new Point[3];			

				for (int i=0;i<3;i++)
				{
					vec.mul(ArrowElement.arrowMat); // やじりの先端座標（１２０度回転）
					edge[i] = new Point();
					edge[i].X = toward_.X + (int)vec.x[0];
					edge[i].Y = toward_.Y + (int)vec.x[1];
					//g.DrawLine(pen_black,toward_,edge[i]);
				}
				Brush br = new SolidBrush(fillColor);
				//HatchBrush br = new HatchBrush(HatchStyle.ForwardDiagonal, Color.Green,
				//	Color.FromArgb(100, Color.Yellow));

				Pen pe = new Pen(fillColor,(int)(0.10*norm));
				pe.DashStyle = dashStyle;
				g.FillPolygon(br,edge);
				g.DrawLine(pe,from_,toward_);
				br.Dispose();
				pe.Dispose();

				highlightDraw(g);
			}
		}

        void anm_AnmFinishedHandler(object sender, EventArgs e)
        {
            foreach (Rectangle r in delayInvalidateQueue)
            {
                Invalidate(r);
            }
            delayInvalidateQueue.Clear();
        }
        public override bool IsInside(int x, int y, bool coerceWheneverFindable)
		{
			if (!findable && !coerceWheneverFindable) return false;

			if (boundingBox.Left <= x && x <= boundingBox.Right
				&& boundingBox.Top <= y && y <= boundingBox.Bottom) return true;
			else return false;
			
			//return false;
		}
		public override void moveRerative(int addx,int addy)
		{
			if(addx !=0 || addy !=0)
			{
				Invalidate();
				setBoundingBox();
				Invalidate();
			}
		}
		public override void Resize(int x,int y,int w,int h)
		{
			/*
			int newW = boundingBox.Width+w,newH = boundingBox.Height+h;
			double d1,d2;
			d1 = Math.Sqrt((double)(boundingBox.Width*boundingBox.Width+
				boundingBox.Height*boundingBox.Height));
			d2 = Math.Sqrt((double)(newW*newW+newH*newH));
			sizeRatio *= (float)(d2/d1);
			if (sizeRatio > 1.0) sizeRatio = 1.0f;
			if (sizeRatio < 0.1) sizeRatio = 0.1f;
			setBoundingBox();
			*/
			if(x !=0 || y!=0 || w!= 0 || h!= 0)	Invalidate();
		}
        public override bool IsPointingEditButton(int x, int y, bool coerceWheneverFindable)
		{
			return false;
		}
		public override void createCopy(out FigureElement fe)
		{
			fe = (FigureElement)(new ArrowElement(this.parentControl,this.priority,this.fromElement,this.towardElement,this.fillColor,this.dashStyle));
		}
		public override void createCreateEvent(FigureElementList elementList,out CreateObjectEvent coe)
		{
			coe = (CreateObjectEvent)(new CreateArrowEvent(elementList,this));
		}
/*
		public override void createCreateEvent(FigureElementList elementList,ref QArrayList eventListToAdd )
		{
			eventListToAdd.Add(new CreateArrowEvent(elementList,this));
		}
*/
		public Color FillColor
		{
			get{ return fillColor;}
			set{fillColor = value;}
		}
	}
	// ***********************
	/*
		public class VirtualPointElement : FigureElement
		{ // 端オブジェクトのない矢印の端に仮想的に作られる。現在使われていない。
			private int X,Y;
			public VirtualPointElement(int x,int y) :base()
			{
				boundingBox.X=x;
				boundingBox.Y=y;
				boundingBox.Width=0;
				boundingBox.Height=0;
				X=x;
				Y=y;
			}
			public override void draw(Graphics g)
			{
			}
			public override bool IsInside(int x,int y)
			{
						if (!findable) return false;

				return (x == X && y == Y);
			}
			public override void moveRerative(int addx,int addy)
			{
				X += addx;
				Y += addy;
				boundingBox.X += addx;
				boundingBox.Y += addy;
			}

		
		}
	*/
	// ******************************
	/*
		public class InkStrokeElement : BitmapElement // これを実装したら、画面の保存でストロークを除くこと！ →済み
		{
			protected Matrix m,wm;

			public InkStrokeElement(Rectangle r,InkCollector ic,Stroke st) :base(r)
			{
				m = new Matrix();
				wm = new Matrix();

				image = new Bitmap(boundingBox.Width,boundingBox.Height);
				Graphics tg = Graphics.FromImage(image);
				//tg.TranslateTransform(-boundingBox.X,-boundingBox.Y,MatrixOrder.Append);
				tg.TranslateTransform(100,100,MatrixOrder.Append);
				// 現在のMatrixを保存
				ic.Renderer.GetViewTransform(ref wm);
				// 倍率は同じで描画領域の左上端に詰める。
				m.Scale(1.0f,1.0f);  
				m.Translate(-st.GetBoundingBox().X,-st.GetBoundingBox().Y,MatrixOrder.Append);
				// 等倍
				ic.Renderer.SetViewTransform(m);
				// インク情報を書き込む
				ic.Renderer.Draw(tg,st,st.DrawingAttributes); 
				// 表示用にMatrixを復元
				ic.Renderer.SetViewTransform(wm);

			}
		}
	*/
	// ******************************
	public class LineElement : WakuElement , IHasFillColor
	{
		public int startX,startY,endX,endY;
		protected Color lineColor;
		public System.Drawing.Drawing2D.DashStyle dashStyle;

		public LineElement(Control parentCont,int prio,Rectangle r,int sx,int sy,int ex,int ey,Color c,System.Drawing.Drawing2D.DashStyle ds) : base(parentCont,prio,r)
		{
			startX=sx;startY=sy;
			endX=ex;endY=ey;
			lineColor = c;
			dashStyle = ds;
            //if (startY == endY) editable = true;
			//if (prio < 0) priority = 2;
		}
		public override void draw(Graphics g)
		{
			if(visible)
			{
				//Pen pe = new Pen(Color.Black,2);
				Pen pe = new Pen(this.lineColor,2);
				pe.DashStyle = dashStyle;
				g.DrawLine(pe,startX,startY,endX,endY);
				pe.Dispose();
				highlightDraw(g);
				EditButtonDraw(g);
			}
		}
		public override void moveRerative(int addx,int addy)
		{
			if(addx !=0 || addy !=0)
			{
				Invalidate();
				x1 += addx;
				x2 += addx;
				y1 += addy;
				y2 += addy;
				setBoundingBox();
				startX += addx;
				startY += addy;
				endX += addx;
				endY += addy;
				Invalidate();
			}
		}	
		public override void Resize(int x,int y,int w,int h)
		{
			if(x !=0 || y!=0 || w!= 0 || h!= 0)
			{
				Invalidate();
				double magx = 1 + (double)w / boundingBox.Width;
				double magy = 1 + (double)h / boundingBox.Height;
				int sx = startX - boundingBox.X;
				int sy = startY - boundingBox.Y;
				int ex = endX - boundingBox.X;
				int ey = endY - boundingBox.Y;

				x1 += x;
				y1 += y;
				x2 += (x + w);
				y2 += (y + h);
				setBoundingBox();

				startX = boundingBox.X + (int)(sx * magx);
				startY = boundingBox.Y + (int)(sy * magy);
				endX = boundingBox.X + (int)(ex * magx);
				endY = boundingBox.Y + (int)(ey * magy);
				Invalidate();
			}
		}	
		public override void createCopy(out FigureElement fe)
		{
			fe = (FigureElement)(new LineElement(this.parentControl,this.priority,this.boundingBox,this.startX,this.startY,this.endX,this.endY,this.lineColor,this.dashStyle));
		}
		public override void createCreateEvent(FigureElementList elementList,out CreateObjectEvent coe)
		{
			coe = (CreateObjectEvent)(new CreateLineEvent(elementList,this));
		}
/*
		public override void createCreateEvent(FigureElementList elementList,ref QArrayList eventListToAdd )
		{
			eventListToAdd.Add(new CreateLineEvent(elementList,this));
		}
*/
		public Color FillColor
		{
			get{return lineColor;}
			set{lineColor = value;}
		}

	}
	// *******************************
	public abstract class WakuAndTextElement : WakuElement
	{
		protected WakuElement wakuElement;
		protected TextElement textElement;

		public WakuAndTextElement(Control parentCont,int prio,Rectangle r,string t,Color c,Font f) : base(parentCont,prio,r)
		{
			textElement = new TextElement(parentCont,prio,r,t,c,f);
            //editable = true;
		}

		public override void draw(Graphics g)
		{
			if (visible)
			{
				WakuDraw(g);
				TextDraw(g);
			}
		}
		public void WakuDraw(Graphics g)
		{
			if (wakuElement != null) wakuElement.draw(g);
		}
		public void TextDraw(Graphics g)
		{
			if (textElement != null) textElement.draw(g);
		}
	}
	// ****************************
	/*
	public class RectangleAndTextElement : WakuAndTextElement
	{
		public RectangleAndTextElement(int prio,Rectangle r,Color wc,string t,Color tc,Font f) : base(prio,r,t,tc,f)
		{
			wakuElement = new RectangleElement(prio,r,wc);

		}
		public override void createCopy(out FigureElement fe)
		{
			fe = (FigureElement)(new RectangleAndTextElement(this.priority,this.boundingBox,((RectangleElement)this.wakuElement).FillColor,this.textElement.Text,this.textElement.FillColor,this.textElement.Font));
		}
		public override void createCreateEvent(FigureElementList elementList, out CreateObjectEvent coe)
		{
			coe = (CreateObjectEvent)(new CreateRectangleAndTextEvent(elementList,this));
		}
	}
	*/
	// ****************************
	public class ElementGroupElement : WakuElement
	{
		protected internal FigureElementList groupElementList;
		public ElementGroupElement(Control parentCont,int prio,Rectangle r,FigureElementList list) : base(parentCont,prio,r)
		{// groupElementListは、呼び出しがわでnewして渡すこと。
			groupElementList = list;
		}

		public override void draw(Graphics g)
		{
			if(visible)
			{
				//groupElementList.draw(g,false);
				highlightDraw(g);
			}
		}

		public override void moveRerative(int addx,int addy)
		{
			groupElementList.moveRerative(addx,addy);
			base.moveRerative(addx,addy);
			
		}
		public override void Resize(int x,int y,int w,int h)
		{
			groupElementList.Resize(boundingBox,x,y,w,h);
			base.Resize(x,y,w,h);
		}

		protected override void highlightDraw(Graphics g)
		{
			if (this.highlight)
			{
				pen_blue.Width = (int)(3 * view.ScreenToViewRatio.X);
				g.DrawRectangle(pen_blue,boundingBox);
				highlightEdgeDraw(g,pen_blue);
			} 
		}
		public override bool IsPointingEditButton(int x,int y,bool coerceWheneverFindable)
		{
			return false;
		}

		public void GetAllGroupedElementList(ref QArrayList al)
		{ // recursive, including itself.
			// Make sure to Crear al before calling this.
			IEnumerator enume = groupElementList.GetEnumerator();
			while(enume.MoveNext())
			{
				if (enume.Current is ElementGroupElement)
				{
					((ElementGroupElement)enume.Current).GetAllGroupedElementList(ref al);
				}
				else
				{
					al.AddNewElement(enume.Current);
				}
			}
			al.AddNewElement(this); // 念のためAddでななく。
			// ElementGroupElement自身は最後に加えられるので、index errorは防げる。
		}


		public override void createCopy(out FigureElement fe)
		{
			FigureElementList newList = new FigureElementList();
			newList.Add(this.groupElementList);
			fe = (FigureElement)(new ElementGroupElement(this.parentControl,this.priority,this.boundingBox,newList));
		}
		public override void createCreateEvent(FigureElementList elementList, out CreateObjectEvent coe)
		{
			coe = (CreateObjectEvent)(new CreateElementGroupEvent(elementList,this));
		}
/*
		public override void createCreateEvent(FigureElementList elementList,ref QArrayList eventListToAdd )
		{
			eventListToAdd.Add(new CreateElementGroupEvent(elementList,this));
		}
*/
	}

	// *******************************
	public class FigureElementList : QBasic.QArrayList
	{
		public FigureElementList() : base()
		{
		}
        public void getPointedElementList(int x, int y, ref FigureElementList fe, bool coerceWheneverFindable)
		{// 「指される」とはIsInsideであり、必ずしもBoundingBox.Containsではない。
			fe.Clear();
			enume = this.GetEnumerator();
			while (enume.MoveNext())
			{
				if( ((FigureElement)enume.Current).IsInside(x,y,coerceWheneverFindable) == true)
				{//add list
					fe.Add(enume.Current);
				}
			}
		}
		public void getPointedBoundingBoxElementList(int x,int y,ref FigureElementList fe)
		{// 単純にBoundingBox.Contaions
			fe.Clear();
			enume = this.GetEnumerator();
			while (enume.MoveNext())
			{
				if( ((FigureElement)enume.Current).BoundingBox.Contains(x,y) == true)
				{//add list
					fe.Add(enume.Current);
				}
			}
		}
        public void getBoxedElementList(Rectangle box, ref FigureElementList fe, bool coerceWheneverFindable)
		{  // boxに包まれる要素リストを返す
			fe.Clear();
			enume = this.GetEnumerator();
			while (enume.MoveNext())
			{
				if( ((FigureElement)enume.Current).IsBoxedBy(box,coerceWheneverFindable) == true)
				{//add list
					fe.Add(enume.Current);
				}
			}
		}
        public void getBoxingElementList(FigureElement rec, ref FigureElementList fe, bool coerceWheneverFindable)
		{ // rec を包む要素リストを返す
			fe.Clear();
			enume = this.GetEnumerator();
			while (enume.MoveNext())
			{
				if (rec.IsBoxedBy(((FigureElement)enume.Current).BoundingBox,coerceWheneverFindable) == true)
				{//add list
					fe.Add(enume.Current);
				}
			}

		}
		public void draw(Graphics g,bool showNoEdittingElement)
		{
			enume = this.GetEnumerator();
			bool highlight;
			while (enume.MoveNext())
			{
				highlight = ((FigureElement)enume.Current).Highlight;
				if (showNoEdittingElement)
				{
					((FigureElement)enume.Current).Highlight = false;
				}
				((FigureElement)enume.Current).draw(g);
				if (showNoEdittingElement)
				{
					((FigureElement)enume.Current).Highlight = highlight;
				}
			}
		}
		public void drawWithoutInk(Graphics g,bool showNoEdittingElement)
		{
			enume = this.GetEnumerator();
			bool highlight;
			while (enume.MoveNext())
			{
				if (enume.Current is StrokeElement)
				{
					// no draw
				}else
				{
					highlight = ((FigureElement)enume.Current).Highlight;
					if (showNoEdittingElement)
					{
						((FigureElement)enume.Current).Highlight = false;
					}
					((FigureElement)enume.Current).draw(g);
					if (showNoEdittingElement)
					{
						((FigureElement)enume.Current).Highlight = highlight;
					}
				}
			}
		}
		public bool Highlight
		{
			set 
			{
				enume = this.GetEnumerator();
				while (enume.MoveNext())
				{
					((FigureElement)enume.Current).Highlight = value;
				}
			}
		}
		public bool Findable
		{
			set 
			{
				enume = this.GetEnumerator();
				while (enume.MoveNext())
				{
					((FigureElement)enume.Current).Findable = value;
				}
			}
		}
		public bool Visible
		{
			set 
			{
				enume = this.GetEnumerator();
				while (enume.MoveNext())
				{
					((FigureElement)enume.Current).Visible = value;
				}
			}
		}
		public void HighlightAnimationStep()
		{
			enume = this.GetEnumerator();
			while (enume.MoveNext())
			{
				((FigureElement)enume.Current).HighlightAnimationStep();
			}
		}
		public void moveRerative(int addx, int addy)
		{
			enume = this.GetEnumerator();
			while (enume.MoveNext())
			{
				((FigureElement)enume.Current).moveRerative(addx,addy);
			}
		}
		public void Resize(Rectangle boxBefore,int x, int y,int w,int h)
		{ // 主にElementGroup用。
			ArrayList incRectList = new ArrayList();
			this.getLinearResizeIncrement(boxBefore,x,y,w,h,ref incRectList);
			Rectangle r;
			for(int i=0;i<this.Count;i++)
			{
				r = (Rectangle)incRectList[i];
				((FigureElement)this[i]).Resize(r.X,r.Y,r.Width,r.Height);
			}
			incRectList.Clear();
		}

		public  void getLinearResizeIncrement(Rectangle boxBefore,int x, int y,int w,int h,ref ArrayList incrementRectList)
		{ // 平行移動と拡大縮小を考えた、グループのリサイズ用の増分リストをかえす。
			incrementRectList.Clear();
			PointF ratio = new PointF((float)(boxBefore.Width + w)/boxBefore.Width
				,(float)(boxBefore.Height+ h)/boxBefore.Height);
			RectangleF difBox = new RectangleF();
			FigureElement fe;
			enume = this.GetEnumerator();
			while (enume.MoveNext())
			{
				fe = (FigureElement)enume.Current;
				difBox.X = fe.BoundingBox.X - boxBefore.X;
				difBox.Y = fe.BoundingBox.Y - boxBefore.Y;
				difBox.Width = fe.BoundingBox.Width;
				difBox.Height = fe.BoundingBox.Height;
				difBox.X *= ratio.X;
				difBox.Y *= ratio.Y;
				difBox.Width *= ratio.X;
				difBox.Height *= ratio.Y;
				difBox.X += (boxBefore.X + x);
				difBox.Y += (boxBefore.Y + y);

				difBox.X -= fe.BoundingBox.X;
				difBox.Y -= fe.BoundingBox.Y;
				difBox.Width -= fe.BoundingBox.Width;
				difBox.Height -= fe.BoundingBox.Height;

				incrementRectList.Add(new Rectangle((int)difBox.X,(int)difBox.Y,(int)difBox.Width,(int)difBox.Height));
			}
		}
		public FigureElement getNearestElement(int x, int y)
		{
			int nearestDist,curDist;
			Point curPoint,p;
			p=new Point(x,y);
			enume = this.GetEnumerator();
			bool NotEmpty = enume.MoveNext();
			if (NotEmpty)
			{
				FigureElement nearest= (FigureElement)enume.Current;
				//curPoint = nearest.MidPoint;
				curPoint = nearest.GetNearestPointOnBoundsFrom(p);
				nearestDist = getDistancePow2(x,y,curPoint.X,curPoint.Y);
				while (enume.MoveNext())
				{
					//curPoint = ((FigureElement)enume.Current).MidPoint;
					curPoint = ((FigureElement)enume.Current).GetNearestPointOnBoundsFrom(p);
					curDist = getDistancePow2(x,y,curPoint.X,curPoint.Y);
					if (curDist < nearestDist)
					{
						nearest = (FigureElement)enume.Current;
						nearestDist = curDist;
					}
				}
				return nearest;
			}
			return null;
		}
		protected static int getDistancePow2(int x1,int y1,int x2,int y2)
		{
			return (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
		}
/*
		public void Invalidate(System.Windows.Forms.Form f)
		{
			enume = this.GetEnumerator();
			Rectangle r = new Rectangle();
			Rectangle s;
			while (enume.MoveNext())
			{
				s = ((FigureElement)enume.Current).BoundingBox;
				r.Location = s.Location;
				r.Size = s.Size;
				r.Inflate(40,40);
				f.Invalidate(r);
				//f.Invalidate(((FigureElement)enume.Current).BoundingBox);
			}
		}

		public void AddToInvalidateRegion(ref Region re,FigureElementList allList,FigureElementList workList)
		{
			workList.Clear();
			enume = this.GetEnumerator();
			Rectangle r = new Rectangle();
			Rectangle s;
			while(enume.MoveNext())
			{
				s = ((FigureElement)enume.Current).BoundingBox;
				r.Location = s.Location;
				r.Size = s.Size;
				r.Inflate(40,40);
				re.Union(r);
				allList.AddRelatedArrowList(ref workList,(FigureElement)enume.Current);
			}
			enume = workList.GetEnumerator();
			while(enume.MoveNext())
			{
				s = ((FigureElement)enume.Current).BoundingBox;
				r.Location = s.Location;
				r.Size = s.Size;
				r.Inflate(40,40); // おそらくたりない。
				re.Union(r);
			}
		}
		*/
		public void AddRelatedArrowList(ref FigureElementList arrowL,FigureElement target)
		{ // 関連する矢印リストを得る。
			//this:検査対象の全集合。矢印やいろいろ混ざってる。
			//arrowL:出力
			//target:今問題にしているオブジェクト
			ArrowElement a;
			//arrowL.Clear();
			IEnumerator enume = this.GetEnumerator();
			while (enume.MoveNext())
			{
				if (((FigureElement)enume.Current).GetType() == typeof(ArrowElement))
				{
					a = (ArrowElement)enume.Current;
					if (((object)a.fromElement) == (object)target ||
						((object)a.towardElement) == (object)target)
					{
						arrowL.Add(a);
					}
				}
			}
			
		}
/*
		public void SetToInvalidateRegion(ref Region re)
		{
			re.MakeEmpty();
			AddToInvalidateRegion(ref re);
		}
*/
        public FigureElement.EDGE PointAnyEdge(int x, int y, bool coerceWheneverFindable)
		{ // かなり適当な関数です。
			enume = this.GetEnumerator();
			FigureElement.EDGE edge;
			while (enume.MoveNext())
			{
				edge = ((FigureElement)enume.Current).PointEdge(x,y,coerceWheneverFindable);
				if (edge != FigureElement.EDGE.NO_EDGE) return edge;
			}
			return FigureElement.EDGE.NO_EDGE;
		}
        public FigureElement GetEditButtonPointedElement(int x, int y, bool coerceWheneverFindable)
		{
			enume = this.GetEnumerator();
			FigureElement fe;
			while (enume.MoveNext())
			{
				fe = (FigureElement)enume.Current;
				if (fe.IsPointingEditButton(x,y,coerceWheneverFindable)) return fe;
			}
			return null;
		}
		public void  GetBoundingBox(ref Rectangle r)
		{ // すべての要素の包含四角形を求める
			enume = this.GetEnumerator();
			FigureElement fe;
			Rectangle box;
			int dif=0;
			if ( this.Count > 0)
			{
				r = ((FigureElement)this[0]).BoundingBox;
				while (enume.MoveNext())
				{
					fe = (FigureElement)enume.Current;
					box = fe.BoundingBox;
					if (r.Left > box.Left)
					{
						dif = r.Left - box.Left;
						r.X= box.X;
						r.Width += dif;
					}
					if (r.Right < box.Right)
					{
						dif = box.Right - r.Right;
						r.Width += dif;
					}
					if (r.Top > box.Top)
					{
						dif = r.Top - box.Top;
						r.Y = box.Y;
						r.Height += dif;
					}
					if (r.Bottom < box.Bottom)
					{
						dif = box.Bottom - r.Bottom;
						r.Height += dif;
					}
				}
			}

		}
		public void  GetSmallestBoundingBox(ref Rectangle r, bool withoutArrow)
		{ // すべての要素のうち、最小のboundingBoxを求める
			enume = this.GetEnumerator();
			FigureElement fe;
			Rectangle box;
			if ( this.Count > 0)
			{
				r = ((FigureElement)this[0]).BoundingBox;
				while (enume.MoveNext())
				{
					fe = (FigureElement)enume.Current;
					if (!withoutArrow || fe.GetType() != typeof(ArrowElement))
					{
						box = fe.BoundingBox;
						if (r.Width > box.Width && r.Height > box.Height)
						{
							r.X = box.X;r.Y = box.Y;
							r.Width = box.Width;r.Height = box.Height;
						}
					}
				}
			}
		}
		public void ResizeAdjust(Rectangle parent,ref Rectangle adjustRect,Size minSize)
		{ // リサイズや移動に伴い、はみ出たりつぶれたりしないように補正する。
			// adjustRectには、希望する変化量が入っている。これを補正する。
			// minSizeは、つぶれないようにする限界量。
			// そもそも変化後が画面よりでかい場合は効果ありません。
			if (adjustRect.Width != 0 || adjustRect.Height != 0)
			{ // つぶれないチェック。
				Rectangle smallest = new Rectangle();
				this.GetSmallestBoundingBox(ref smallest,true);
				if (smallest.Width +adjustRect.Width < minSize.Width)
				{
					adjustRect.Width = minSize.Width - smallest.Width;
				}
				if (smallest.Height +adjustRect.Height < minSize.Height)
				{
					adjustRect.Height = minSize.Height - smallest.Height;
				}
			}
//			if (adjustRect.X != 0 || adjustRect.Y != 0 )
//			{// はみ出ないチェック。
//				Rectangle bounds = new Rectangle();
//				this.GetBoundingBox(ref bounds);
//				bounds.X += adjustRect.X;
//				bounds.Y += adjustRect.Y;
//				bounds.Width += adjustRect.Width;
//				bounds.Height += adjustRect.Height;
//				Point adjustment = new Point();
//				QBasic.DrawingUtil.ForceBoxInBounds(bounds,
//					parent,ref adjustment);
//
//				adjustRect.X += adjustment.X;
//				adjustRect.Y += adjustment.Y;
//			}


		}
        public void ResizeAdjustAspectPreserve(Rectangle parent, ref Rectangle adjustRect, Size minSize,float aspectWidthOverHeight)
        { // アスペクト比をたもちつつResizeAdjust
            ResizeAdjust(parent, ref adjustRect, minSize);
            int adjH = (int)(adjustRect.Width * aspectWidthOverHeight);
            adjustRect.Height = adjH;
        }
        public void GetArrowPointedListOf(FigureElement fe, ref FigureElementList outFromList, ref FigureElementList outTowardList)
		{
			ArrowElement ae;
			outFromList.Clear();
			outTowardList.Clear();
			enume = this.GetEnumerator();
			while(enume.MoveNext())
			{
				if (enume.Current is ArrowElement)
				{
					ae = (ArrowElement)enume.Current;
					if (ae.fromElement.Equals(fe))
					{
						outFromList.Add(ae);
					}
					if (ae.towardElement.Equals(fe))
					{
						outTowardList.Add(ae);
					}
				}
			}
		}
		public void CreateCopy(ref FigureElementList fel)
		{
			FigureElement fe,ce;
			fel.Clear();
			enume = this.GetEnumerator();
			while(enume.MoveNext())
			{
				fe = (FigureElement)enume.Current;
				fe.createCopy(out ce);
				fel.Add(ce);
			}
		}
		public void CreateCreateEventList(FigureElementList orgList,ref QArrayList outList)
		{
			CreateObjectEvent coe;
			FigureElement fe;
			outList.Clear();
			enume = this.GetEnumerator();
			while(enume.MoveNext())
			{
				fe = (FigureElement)enume.Current;
				fe.createCreateEvent(orgList,out coe);
				outList.Add(coe);
			}
		}

	}

	// ******************
	public class FigureElementMidPointComparer : IComparer
	{
		private bool compareX;
		public FigureElementMidPointComparer(bool cX)
		{
			compareX = cX;
		}
		public int Compare (object a, object b) 
		{
			if (!(a is FigureElement && b is FigureElement)) 
			{
				throw new ArgumentException
					("The objects to compare must be of type 'FigureElement'");
			}
			int ax=0,bx=0;
			if (compareX)
			{
				ax = ((FigureElement)a).MidPoint.X;
				bx = ((FigureElement)b).MidPoint.X;
			}
			else
			{
				ax = ((FigureElement)a).MidPoint.Y;
				bx = ((FigureElement)b).MidPoint.Y;
			}
			return ax.CompareTo(bx);
		}
	}
	// ******************
	public class FigureElementPriorityComparer : IComparer
	{
		public FigureElementPriorityComparer()
		{
		}
		public int Compare (object a, object b) 
		{
			if (!(a is FigureElement && b is FigureElement)) 
			{
				throw new ArgumentException
					("The objects to compare must be of type 'FigureElement'");
			}
			int ax=0,bx=0;
			ax = ((FigureElement)a).Priority;
			bx = ((FigureElement)b).Priority;
			return ax.CompareTo(bx);
		}
	}
}
